<template>
  <view
    class="overlay"
    hover-stop-propagation
    :style="[getMaskStyle, getZoomStyle, getRootStyle]"
    :class="[
      {
        'overlay--show': visible,
        'overlay--round': round,
        'overlay--mask': overlay,
      },
      `overlay--${position}`,
    ]"
    @tap.stop="handleClick"
    @touchstart="handleTouchstart"
    @touchmove.stop.prevent="handleTouchmove"
    @touchend="handleTouchend"
    @touchcancel="handleTouchend"
  >
    <view class="overlay__bg" @touchmove.stop.prevent="() => {}"></view>
    <view class="overlay__container" :style="[getBoxStyle]" @click.stop.prevent="() => {}">
      <view class="overlay__header">
        <slot name="header" />
      </view>
      <view class="overlay__body" :class="{ 'overlay__body--min': min }">
        <scroll-view style="width: 100%; height: 100%" :scroll-y="!min" @scrolltolower="handleScrolltolower">
          <view class="overlay__body__inner">
            <slot />
          </view>
        </scroll-view>
      </view>
      <view class="overlay__footer">
        <slot name="footer" />
      </view>
      <view
        v-if="closeable"
        class="overlay__close"
        :class="[`overlay__close--${closeIconPosition}`]"
        :style="[getCloseStyle]"
        @click="handleClose"
      >
        <m-icon :name="closeIcon" :size="closeIconSize" :color="closeColor" />
      </view>
    </view>
  </view>
</template>

<script lang="ts">
import { defineComponent, computed, reactive, toRefs, inject, watch } from '@vue/composition-api'
import addUnit from '@/utils/add-unit'
import getCurrentComponent from '@/utils/get-current-component'
import boundingClientRect from '@/utils/bounding-client-rect'
import addColor from '@/utils/add-color'

const overlayProps = {
  visible: {
    type: Boolean,
    default: false,
  },
  zIndex: {
    type: [Number, String],
    default: 999,
  },
  duration: {
    type: [Number, String],
    default: 0.3,
  },
  overlay: {
    type: Boolean,
    default: true,
  },
  overlayBackground: {
    type: String,
    default: 'rgba(0, 0, 0, 0.6)',
  },
  overlayStyle: {
    type: Object,
    default: () => ({}),
  },
  closeOnClickOverlay: {
    type: Boolean,
    default: true,
  },
  position: {
    type: String,
    default: 'center',
  },
  width: {
    type: [String, Number],
    default: 'auto',
  },
  height: {
    type: [String, Number],
    default: 'auto',
  },
  round: {
    type: Boolean,
    default: true,
  },
  closeable: {
    type: Boolean,
    default: false,
  },
  background: {
    type: String,
    default: '#fff',
  },
  closeIconPosition: {
    type: String,
    default: 'top-right',
  },
  closeColor: {
    type: String,
    default: '#666',
  },
  closeIcon: {
    type: String,
    default: 'close',
  },
  closeIconSize: {
    type: [Number, String],
    default: 20,
  },
  push: {
    type: Boolean,
    default: false,
  },
  lockScroll: {
    type: Boolean,
    default: true,
  },
}

export { overlayProps }

export default defineComponent({
  props: overlayProps,
  emits: ['update:visible', 'close'],
  setup(props, { emit }) {
    const vm = getCurrentComponent()
    const windowLockScroll: any = inject('windowLockScroll', null)
    const state = reactive({
      startY: 0,
      rectInner: {} as any,
      translateY: 0,
      status: '',
      min: false,
    })
    const getMaskStyle = computed(() => {
      let style: any = {}

      if (props.visible) style.zIndex = props.zIndex ? props.zIndex : 2000
      else style.zIndex = -1

      if (!props.overlay) {
        return style
      }

      style.backgroundColor = addColor(props.overlayBackground)
      style.transition = `all ${+props.duration}s ease-in-out`

      // 判断用户传递的对象是否为空，不为空就进行合并
      if (Object.keys(props.overlayStyle).length)
        style = {
          ...style,
          ...props.overlayStyle,
        }

      return style
    })
    const handleClick = (e: MouseEvent) => {
      emit('click', e)
      if (props.closeOnClickOverlay) {
        handleClose()
      }
    }
    const getBoxStyle = computed(() => {
      const obj: any = { background: addColor(props.background) }

      if (props.overlay) {
        obj.width = addUnit(props.width)
        obj.height = addUnit(props.height)
      } else {
        obj.width = '100%'
        obj.height = '100%'
      }

      if (props.position === 'bottom') {
        if (props.visible) {
          if (props.overlay) {
            obj.transform = `translateY(${state.translateY}rpx) translateX(-50%)`
          } else {
            obj.transform = `translateY(0) translateX(-50%)`
          }
        } else {
          obj.transform = `translateY(100%) translateX(-50%)`
        }
      } else if (props.position === 'top') {
        if (props.visible) {
          obj.transform = `translateY(0) translateX(-50%)`
        } else {
          obj.transform = `translateY(-100%) translateX(-50%)`
        }
      } else if (props.position === 'left') {
        if (props.visible) {
          obj.transform = `translateX(0)`
        } else {
          obj.transform = `translateX(-100%)`
        }
      } else if (props.position === 'right') {
        if (props.visible) {
          obj.transform = `translateX(0)`
        } else {
          obj.transform = `translateX(100%)`
        }
      }

      return obj
    })
    const getZoomStyle = computed(() => {
      if (props.position === 'center') {
        return props.visible ? { transform: 'scale(1, 1)' } : { transform: 'scale(1.2, 1.2)' }
      }
      return {}
    })
    const handleClose = () => {
      emit('update:visible', false)
      emit('close')
    }
    const getCloseStyle = computed(() => {
      if (props.closeIconPosition === 'top-right') {
        return {
          top: '32rpx',
          right: '24rpx',
          color: '#f5f5f5',
        }
      } else if (props.closeIconPosition === 'top-left') {
        return {
          top: '32rpx',
          left: '24rpx',
          color: '#f5f5f5',
        }
      } else {
        return {
          bottom: '-96rpx',
          color: '#ffffff',
          left: '50%',
          transform: 'translateX(-50%)',
        }
      }
    })

    const handleTouchstart = async (e: TouchEvent) => {
      state.startY = e.changedTouches[0].clientY
      state.rectInner = await boundingClientRect('.overlay__body__inner', vm)
    }

    const handleTouchmove = (e: TouchEvent) => {
      e.stopPropagation()

      if (props.lockScroll) e.preventDefault()

      if (!props.push) {
        return
      }

      const clientY = e.changedTouches[0].clientY
      const end = clientY - state.startY

      const distance = 30

      // 下拉
      if (end > 0) {
        const top = state.rectInner && state.rectInner.top ? state.rectInner.top : 0
        if (end + top > distance) {
          e.preventDefault()
          state.status = 'pull-down'
          state.translateY = end
        } else {
          state.status = ''
        }
      } else if (end < 0) {
        const top = state.rectInner && state.rectInner.top ? state.rectInner.top : 0
        if (end < -distance) {
          e.preventDefault()
          state.status = 'pull-up'
          state.translateY = end + top
        } else {
          state.status = ''
        }
      }
    }

    const handleTouchend = () => {
      state.translateY = 0

      if (state.status === 'pull-down') {
        state.min = true
        state.translateY = 700
      } else if (state.status === 'pull-up') {
        state.translateY = 0
        state.min = false
      }
    }

    const handleScrolltolower = () => {
      emit('scrolltolower')
    }

    const getRootStyle = computed(() => {
      if (props.overlay) {
        return {}
      }

      const obj: any = {
        width: addUnit(props.width),
        height: addUnit(props.height),
        transform: `translateY(${state.translateY}rpx)`,
      }

      if (props.position === 'bottom') {
        obj.top = 'auto'
      } else if (props.position === 'top') {
        obj.bottom = 'auto'
      } else if (props.position === 'left') {
        obj.right = 'auto'
      } else if (props.position === 'right') {
        obj.left = 'auto'
      }

      return obj
    })

    const handleDisabledScroll = () => {
      if (props.lockScroll) {
        return
      }
    }

    watch(
      () => props.visible,
      (newVal) => {
        windowLockScroll && windowLockScroll.setLockScroll(newVal && props.lockScroll)
      }
    )

    return {
      ...toRefs(state),
      getRootStyle,
      handleScrolltolower,
      handleTouchstart,
      handleTouchmove,
      handleTouchend,
      getMaskStyle,
      handleClick,
      getZoomStyle,
      getBoxStyle,
      handleClose,
      getCloseStyle,
      handleDisabledScroll,
    }
  },
})
</script>
<style lang="scss" scoped>
.overlay {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  opacity: 0;
  transition: transform 0.3s;

  &__bg {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
  }

  &__container {
    position: absolute;
    // overflow: hidden;
    display: flex;
    flex-direction: column;
  }
  &__header {
    text-align: center;
  }
  &__body {
    min-height: 0;
    flex: 1;
    position: relative;
    &--min {
      &::after {
        content: '';
        position: absolute;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        background-color: rgba(0, 0, 0, 0);
        overflow: hidden;
      }
    }
  }
  &__close {
    position: absolute;
    z-index: 2;
    width: 40rpx;
    height: 40rpx;
    // background-color: $--bg-body-c;
    border-radius: 50%;
    text-align: center;
    line-height: 38rpx;
    &--bottom {
      background-color: transparent;
    }
  }

  &--show {
    opacity: 1;
  }
  &--center {
    .overlay__container {
      top: 50%;
      left: 50%;
      transform: translateX(-50%) translateY(-50%);
    }
  }
  &--top {
    .overlay__container {
      position: absolute;
      top: 0;
      left: 50%;
      transform: translateY(-100%) translateX(-50%);
      transition: transform 0.3s;
    }
  }
  &--bottom {
    .overlay__container {
      position: absolute;
      bottom: 0;
      left: 50%;
      transform: translateY(100%) translateX(-50%);
      transition: transform 0.3s, height 0.3s;
    }
  }
  &--left {
    .overlay__container {
      position: absolute;
      left: 0;
      top: 0;
      bottom: 0;
      transform: translateX(-100%);
      transition: transform 0.3s;
    }
  }
  &--right {
    .overlay__container {
      position: absolute;
      right: 0;
      top: 0;
      bottom: 0;
      transform: translateX(-100%);
      transition: transform 0.3s;
    }
  }
  &--round {
    &.overlay--center .overlay__container {
      border-radius: 16rpx;
    }
    &.overlay--top .overlay__container {
      border-radius: 0 0 16rpx 16rpx;
    }
    &.overlay--bottom .overlay__container {
      border-radius: 16rpx 16rpx 0 0;
    }
    &.overlay--left .overlay__container {
      border-radius: 0 16rpx 16rpx 0;
    }
    &.overlay--right .overlay__container {
      border-radius: 16rpx 0 0 16rpx;
    }
  }
}
</style>
